りおんクロニクル


SQLite × マルチDB対応設計|将来のDB移行に強いアーキテクチャ【2026年版】

Home【2026年版】C# / .NET入門と実践ガイド|基礎・業務アプリ開発・SQLite連携まで体系的に解説

アプリ開発の初期はSQLiteで十分でも、 ユーザー数増加・データ量増加・サーバー化などのタイミングで SQL Server / PostgreSQL へ移行したいというケースは非常に多いです。 しかし、DB依存のコードがアプリ全体に散らばっていると、移行コストは爆発します。

そこで重要なのが、最初からマルチDB対応のアーキテクチャを設計しておくこと。 この記事では、SQLiteを起点に、将来のDB移行に強い構成を実務目線で解説します。

この記事でわかること
・マルチDB対応の基本戦略
・Repository + Unit of Work の役割
・SQL抽象化の方法(Dapper/EF Core)
・接続層・SQL層の分離
・DBごとの差異(LIMIT/OFFSET、AUTO INCREMENTなど)
・業務アプリ向けベストプラクティス

1. マルチDB対応の基本戦略

マルチDB対応の本質は、次の3つを分離することです。

  1. 接続層(Connection Factory)
  2. SQL層(Repository)
  3. トランザクション層(Unit of Work)

この3つを分離すれば、DB移行時に差し替えるのは最小限で済みます。

2. 接続層(Connection Factory)を分離する

DBごとに接続クラスが異なるため、 接続生成を1箇所に集約するのが最重要です。

■ IConnectionFactory

public interface IConnectionFactory
{
    IDbConnection CreateConnection();
}

■ SQLite実装

public class SqliteConnectionFactory : IConnectionFactory
{
    private readonly string _cs;

    public SqliteConnectionFactory(string cs) => _cs = cs;

    public IDbConnection CreateConnection()
        => new SqliteConnection(_cs);
}

■ SQL Server実装

public class SqlServerConnectionFactory : IConnectionFactory
{
    private readonly string _cs;

    public SqlServerConnectionFactory(string cs) => _cs = cs;

    public IDbConnection CreateConnection()
        => new SqlConnection(_cs);
}

アプリ側は DB の種類を一切意識しません。

3. Repository層でSQLを抽象化する

RepositoryはDBごとの差異を吸収する層です。 UIやサービス層はRepositoryだけを呼び出します。

■ IUserRepository

public interface IUserRepository
{
    Task<User?> GetByIdAsync(int id);
    Task<IEnumerable<User>> GetAllAsync();
    Task AddAsync(User user);
}

■ SQLite実装(Dapper)

public class SqliteUserRepository : IUserRepository
{
    private readonly IConnectionFactory _factory;

    public SqliteUserRepository(IConnectionFactory factory)
        => _factory = factory;

    public async Task<User?> GetByIdAsync(int id)
    {
        using var con = _factory.CreateConnection();
        return await con.QueryFirstOrDefaultAsync<User>(
            "SELECT Id, Name, Age FROM Users WHERE Id=@id", new { id });
    }
}

■ SQL Server実装(Dapper)

SQL Serverでは LIMIT が使えないため、TOP を使うなど差異を吸収します。

public class SqlServerUserRepository : IUserRepository
{
    private readonly IConnectionFactory _factory;

    public SqlServerUserRepository(IConnectionFactory factory)
        => _factory = factory;

    public async Task<User?> GetByIdAsync(int id)
    {
        using var con = _factory.CreateConnection();
        return await con.QueryFirstOrDefaultAsync<User>(
            "SELECT TOP 1 Id, Name, Age FROM Users WHERE Id=@id", new { id });
    }
}

4. Unit of Workでトランザクションを統合

Unit of Work(UoW)は、 複数Repositoryの操作を1つのトランザクションにまとめる仕組みです。

■ IUnitOfWork

public interface IUnitOfWork : IAsyncDisposable
{
    IUserRepository Users { get; }
    IOrderRepository Orders { get; }

    Task CommitAsync();
    Task RollbackAsync();
}

■ SQLite実装

public class SqliteUnitOfWork : IUnitOfWork
{
    private readonly IDbConnection _con;
    private readonly IDbTransaction _tran;

    public IUserRepository Users { get; }
    public IOrderRepository Orders { get; }

    public SqliteUnitOfWork(IConnectionFactory factory)
    {
        _con = factory.CreateConnection();
        _con.Open();
        _tran = _con.BeginTransaction();

        Users = new SqliteUserRepository(factory);
        Orders = new SqliteOrderRepository(factory);
    }

    public Task CommitAsync() => Task.Run(() => _tran.Commit());
    public Task RollbackAsync() => Task.Run(() => _tran.Rollback());

    public ValueTask DisposeAsync()
    {
        _tran.Dispose();
        _con.Dispose();
        return ValueTask.CompletedTask;
    }
}

SQL Server版も同じ構造で作れます。

5. DBごとの差異をどう吸収するか?

マルチDB対応で最も重要なのは、DBごとの差異をRepositoryで吸収すること。

■ 代表的な差異一覧

項目SQLiteSQL ServerPostgreSQL
自動採番INTEGER PRIMARY KEYIDENTITYSERIAL / IDENTITY
LIMITLIMITTOP / OFFSET FETCHLIMIT
日付TEXT(ISO8601)datetime2timestamp
外部キーPRAGMA foreign_keys=ON標準標準

これらはすべてRepository層で吸収すれば、 アプリ本体はDB差異を一切意識しなくて済みます。

6. 実務アプリ向けベストプラクティス

まとめ:マルチDB対応は“最初の設計”で決まる

「今はSQLiteだけど、将来どうなるかわからない」 そんなアプリこそ、マルチDB対応設計が必須です。 この記事のアーキテクチャをベースに、 長期運用に耐えるデータアクセス層を構築してみてください。

前のページ  次のページ